﻿<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script src="../Resource/JS/jquery-1.10.2.js"></script>
</head>
<body>
    <svg id="svg1" width="100%" height="900px" version="1.1"
        xmlns="http://www.w3.org/2000/svg">
        <defs>
            <marker id="markerArrow" markerWidth="10" markerHeight="11" refX="2" refY="6" orient="auto">
                <path d="M2,2 L2,11 L10,6 L2,2" style="fill: #000000;" />
            </marker>
        </defs>

    </svg>
    <button onclick="R1();">set node6</button>
</body>
</html>
<script>
    //https://github.com/mbostock/d3/wiki/Api-%E5%8F%82%E8%80%83
    function RenderView() {
        this._diagramLst = [];
        // add, delete , get
    }

    function Diagram(domEl) {
        this._domEl = domEl;
        //解决鼠标事件中this作用域的标识
        this._domEl.tag = this;

        //所有对象的集合
        this._elementList = [];
        //点击选中的元素
        this._selectedElement = null;
        // 选中元素之后，选择位于元素中的偏移坐标
        this._selectedOffset = null;

        //添加所有的元素(node和line)到集合
        this.addElement = function (el) {
            this._elementList.push(el);
            if (el._parent == undefined || el._parent == null) {
                this._domEl.appendChild(el.getDomEl());
            }
            else {
                el._parent.getDomEl().appendChild(el.getDomEl());
            }
            el.updateLayout();
        }

        //click event
        this._domEl.addEventListener("click", function (e) {
            //alert(e.target.tagName);
        }, false);

        //所有对象的鼠标事件操作
        this._domEl.onmousedown = function (e) {
            //获取当前类的this
            var diagram = this.tag;
            //鼠标当前位置x,y
            var mousePos = { x: e.clientX, y: e.clientY };
            //从最下面往外层循环，svg添加元素是按照顺序来的，获取最上面的点击元素
            for (var i = diagram._elementList.length - 1; i >= 0; i--) {
                //diagram._elementList里面还有线条，线条是没有hitTest方法的，这里判断一下
                if (diagram._elementList[i].hitTest != undefined) {
                    //获取当前点击对象的偏移值以及是否被选中的标识
                    var hitResult = diagram._elementList[i].hitTest(mousePos);
                    //如果选中
                    if (hitResult.isHit) {
                        //当前选中的对象赋值
                        diagram._selectedElement = diagram._elementList[i];
                        //get偏移值
                        diagram._selectedOffset = { x: hitResult.offsetX, y: hitResult.offsetY };
                        break;
                    }
                }
            }
        }
        this._domEl.onmousemove = function (e) {
            //获取当前类的this
            var diagram = this.tag;
            //判断是否有选中的对象
            if (diagram._selectedElement != null) {
                //计算鼠标的偏移值
                var mousePos = { x: e.clientX + diagram._selectedOffset.x, y: e.clientY + diagram._selectedOffset.y };
                //重新设置绝对坐标,坐标转换成this._position
                diagram._selectedElement.setActualPosition(mousePos);
                //更新布局.集成自Node父类
                diagram._selectedElement.updateLayout();
            }
        }
        this._domEl.onmouseup = function () {
            var diagram = this.tag;
            diagram._selectedElement = null;
        }
    }

    //Node和Line的基类
    function Element() {
        this.updateLayout = function () { }
        this.getDomEl = function () { }
        this.getPosition = function () { };
        this.getSize = function () { };
        this.setPosition = function (pos) { };
        this.setSize = function (size) { };
    }

    //每次选中后，都计算偏移值的result
    function HitResult(hit, x, y) {
        this.isHit = hit;
        this.offsetX = x;
        this.offsetY = y;
    }

    //Rect Circle Container等元素的父类
    function Node(domEl, position, size) {
        this._domEl = domEl;
        Element.apply(this, []);

        this._position = position;
        this._size = size;
        this._parent = null;

        this.getDomEl = function () {
            return this._domEl;
        }

        //线条的集合,每个Node都可能会有线条链接，这里记录下来
        this._linkList = new Array();

        //add line
        this.addLink = function (link) {
            this._linkList.push(link);
        }

        //获取元素的所有连接点
        this.getLinkPos = function () {
            return [];
        }

        this.setPosition = function (pos) {
            this._position = pos;
        }

        this.setSize = function (size) {
            this._size = size;
        }

        this.getSize = function () {
            return this._size;
        }

        //更新自己位置和line的位置,所有继承这个Node类的子类都必须实现这个方法
        this.updateLayout = function () {
            //更新位置.必须先获取对象的绝对坐标
            this.onUpdateLayout();
            for (var i = 0; i < this._linkList.length; i++) {
                this._linkList[i].updateLayout();
            }
        };

        this.onUpdateLayout = function () { };
        this.getCenterPos = function () { };

        // 测试pos是否在元素有效范围内
        // return HitResult
        // 所有继承这个Node的子类都必须实现这个方法
        this.hitTest = function (pos) { };

        //获取绝对坐标方法,递归所有父节点
        this.getActualPosition = function () {
            var pos = { x: this._position.x, y: this._position.y };
            var parent = this._parent;
            while (parent != null) {
                pos.x += parent._position.x;
                pos.y += parent._position.y;
                parent = parent._parent;
            }
            return pos;
        }

        //设置绝对坐标,根据当前位置转换成相对坐标  this._position是相对坐标,所有元素的位置都是相对坐标
        this.setActualPosition = function (actualPos) {
            var parent = this._parent;
            if (parent == null) {
                this._position = actualPos;
            }
            else {
                var pactual = this._parent.getActualPosition();
                //控制容器内的元素不能移动出容器
                if (actualPos.x >= pactual.x
                    && actualPos.x + this._size.w <= pactual.x + this._parent.getSize().w
                    && actualPos.y >= pactual.y
                    && actualPos.y + this._size.h <= pactual.y + this._parent.getSize().h) {
                    var pos = { x: actualPos.x, y: actualPos.y };
                    while (parent != null) {
                        pos.x -= parent._position.x;
                        pos.y -= parent._position.y;
                        parent = parent._parent;
                    }

                    this._position.x = pos.x;
                    this._position.y = pos.y;
                }
            }
        }
    }

    //容器
    function NodeContainer(position, size) {
        Node.apply(this, [document.createElementNS('http://www.w3.org/2000/svg', "rect"), position, size]);
        this.children = [];

        //添加Node到容器里面
        this.addChildren = function (node) {
            this.children.push(node);
            node._parent = this;
        }

        //设置常规属性
        this._domEl.setAttribute("fillOpacity", "0.5");
        this._domEl.setAttribute("fill", "white");
        this._domEl.setAttribute("stroke", "red");
        this._domEl.setAttribute("strokeWidth", 1);
        this._domEl.setAttribute("rx", "7.5");
        this._domEl.setAttribute("ry", "7.5");

        //更新自己位置和children的位置
        this.onUpdateLayout = function () {
            this._domEl.setAttribute("x", this.getActualPosition().x);
            this._domEl.setAttribute("y", this.getActualPosition().y);
            this._domEl.setAttribute("width", this._size.w);
            this._domEl.setAttribute("height", this._size.h);

            for (var i = 0; i < this.children.length; i++) {
                this.children[i].updateLayout();
            }
        }

        //点击事件方法，返回是否点击以及偏移坐标
        this.hitTest = function (pos) {
            var actualPosition = this.getActualPosition();
            if (pos.x > actualPosition.x
                && pos.y > actualPosition.y
                && pos.x < actualPosition.x + this.getSize().w
                && pos.y < actualPosition.y + this.getSize().h) {
                return new HitResult(true, actualPosition.x - pos.x, actualPosition.y - pos.y);
            }
            return new HitResult(false, 0, 0);
        }
    }

    //矩形
    // 继承Node  并且实现更新自己，点击事件，得到中心点（连线需要中心点坐标）
    function Rectangle(position, size) {
        Node.apply(this, [document.createElementNS('http://www.w3.org/2000/svg', "rect"), position, size]);

        this.onUpdateLayout = function () {
            this._domEl.setAttribute("x", this.getActualPosition().x);
            this._domEl.setAttribute("y", this.getActualPosition().y);
            this._domEl.setAttribute("width", this._size.w);
            this._domEl.setAttribute("height", this._size.h);
        };

        //fill:red;stroke:black;
        //stroke-width:5;opacity:0.5
        this._domEl.style.fillOpacity = "0.5";
        this._domEl.style.fill = "white";
        this._domEl.style.stroke = "black";
        this._domEl.style.strokeWidth = "1";
        this._domEl.setAttribute("rx", "20");
        this._domEl.setAttribute("ry", "20");

        //this.getLinkPos = function () {
        //    return [];
        //}

        this.hitTest = function (pos) {
            var actualPosition = this.getActualPosition();
            if (pos.x > actualPosition.x
                && pos.y > actualPosition.y
                && pos.x < actualPosition.x + this.getSize().w
                && pos.y < actualPosition.y + this.getSize().h) {
                return new HitResult(true, actualPosition.x - pos.x, actualPosition.y - pos.y);
            }
            return new HitResult(false, 0, 0);
        }

        this.getCenterPos = function () {
            var x = this._position.x + (this._size.w) / 2;
            var y = this._position.y + (this._size.h) / 2;
            return { x: x, y: y };
        }
    }

    // image in svg 实现思路和矩形一样
    function Image(position, size, src) {
        Node.apply(this, [document.createElementNS('http://www.w3.org/2000/svg', "image"), position, size]);
        this._domEl.href.baseVal = src;
        this.onUpdateLayout = function () {
            this._domEl.setAttribute("x", this.getActualPosition().x);
            this._domEl.setAttribute("y", this.getActualPosition().y);
            this._domEl.setAttribute("height", this._size.h + "px");
            this._domEl.setAttribute("width", this._size.w + "px");
        };

        this.getLinkPos = function () {
            return [];
        }

        this.hitTest = function (pos) {
            var actualPosition = this.getActualPosition();
            if (pos.x > actualPosition.x
                && pos.y > actualPosition.y
                && pos.x < actualPosition.x + this.getSize().w
                && pos.y < actualPosition.y + this.getSize().h) {
                return new HitResult(true, actualPosition.x - pos.x, actualPosition.y - pos.y);
            }
            return new HitResult(false, 0, 0);
        }

        this.getCenterPos = function () {
            var x = this.getActualPosition().x + (this._size.w) / 2;
            var y = this.getActualPosition().y + (this._size.h) / 2;
            return { x: x, y: y };
        }
    }

    function Circle(position, radius) {
        Node.apply(this, [document.createElementNS('http://www.w3.org/2000/svg', "circle"), position, { w: radius, h: radius }]);

        this.onUpdateLayout = function () {
            this._domEl.setAttribute("cx", this._position.x);
            this._domEl.setAttribute("cy", this._position.y);
            this._domEl.setAttribute("r", this._size.w);
        };
        this.getLinkPos = function () {
            return [];
        }
        this.hitTest = function (pos) {
            if (pos.x > this.getPosition().x
                && pos.y > this.getPosition().y
                && pos.x < this.getPosition().x + this.getSize().w
                && pos.y < this.getPosition().y + this.getSize().h) {
                return true;
            }
            return false;
        }

        this._position = this.getPosition();
        this._size = this.getSize();

        this.getCenterPos = function () {
            var x = this._position.x + this._size.w;
            var y = this._position.y + this._size.w;
            return { x: x, y: y };
        }
    }


    //line 实现思路和矩形一样，多了个计算线的方法
    function Link(from, fromPosIndex, to, toPosIndex) {

        this._domEl = document.createElementNS('http://www.w3.org/2000/svg', "polyline")
        this._domEl.style.strokeWidth = 2;
        this._domEl.style.stroke = "red";
        this._domEl.style.fill = "transparent";
        this._domEl.style.markerEnd = "url(#markerArrow)";

        Element.apply(this, []);

        this._from = from;
        this._to = to;
        this._fromPosIndex = fromPosIndex;
        this._toPosIndex = toPosIndex;
        this._from.addLink(this);
        this._to.addLink(this);

        this.getDomEl = function () {
            return this._domEl;
        }

        this.updateLayout = function () {
            //var fromPos = this._from.getLinkPos()[this._fromPosIndex];
            //var toPos = this._to.getLinkPos()[this._toPosIndex];
            //更新el的位置信息
            var path = this.calcPath();
            this._domEl.setAttribute("points", path);
        }

        this.calcPath = function () {
            var f = this._from.getDomEl();
            var t = this._to.getDomEl();
            var x1 = this._from.getActualPosition().x;
            var y1 = this._from.getActualPosition().y;
            var w1 = this._from.getSize().w;
            var h1 = this._from.getSize().h;
            var x2 = this._to.getActualPosition().x;
            var y2 = this._to.getActualPosition().y;
            var w2 = this._to.getSize().w;
            var h2 = this._to.getSize().h;

            var point1 = this._from.getCenterPos();
            var point2 = this._to.getCenterPos();

            var points = "";
            //rect1在rect2的正上方
            if (point1.x == point2.x && point1.y < point2.y) {
                points = (x1 + w1 / 2) + " " + (y1 + h1) + "," + (x1 + w1 / 2) + " " + y2;
            }
            //rect1在rect2的左上方
            if (point1.x < point2.x && point1.y < point2.y) {
                points = (x1 + w1 / 2) + " " + (y1 + h1) + "," + (x1 + w1 / 2) + " " + (y2 + h2 / 2) + "," + (x2) + " " + (y2 + h2 / 2);
            }
            //rect1在rect2的右上方
            if (point1.x > point2.x && point1.y < point2.y) {
                points = (x1 + w1 / 2) + " " + (y1 + h1) + "," + (x1 + w1 / 2) + " " + (y2 + h2 / 2) + " " + (x2 + w2) + " " + (y2 + h2 / 2);
            }
            //rect1在rect2的正左边
            if (point1.x < point2.x && point1.y == point2.y) {
                points = (x1 + w1) + " " + (y1 + h1 / 2) + "," + x2 + " " + (y2 + h2 / 2);
            }
            //rect1在rect2的左下方
            if (point1.x < point2.x && point1.y > point2.y) {
                points = (x1 + w1) + " " + (y1 + h1 / 2) + "," + (x2 + w2 / 2) + " " + (y1 + h1 / 2) + "," + (x2 + w2 / 2) + " " + (y2 + h2);
            }
            //rect1在rect2的正下方
            if (point1.x == point2.x && point1.y > point2.y) {
                points = (x1 + w1 / 2) + " " + y1 + "," + (x1 + w1 / 2) + " " + (y2 + h2);
            }
            //rect1在rect2的右下方
            if (point1.x > point2.x && point1.y > point2.y) {
                points = x1 + " " + (y1 + h1 / 2) + "," + (y2 + w2 / 2) + " " + (y1 + h1 / 2) + "," + (x2 + w2 / 2) + " " + (y2 + h2);
            }
            //rect1在rect2的正右方
            if (point1.x > point2.x && point1.y == point2.y) {
                points = x1 + " " + (y1 + h1 / 2) + "," + (x2 + w2) + " " + (y1 + h1 / 2);
            }
            return points;
        }
    }

    var diagram = new Diagram(document.getElementById('svg1'));
    var node1 = new Image({ x: 10, y: 10 }, { w: 48, h: 48 }, "resource/images/manlian.png");
    var node2 = new Image({ x: 60, y: 90 }, { w: 48, h: 48 }, "resource/images/qieerxi.png");
    var node3 = new Image({ x: 320, y: 100 }, { w: 100, h: 100 }, "resource/images/huangma.png");
    var node4 = new Image({ x: 500, y: 10 }, { w: 100, h: 100 }, "resource/images/huangma.png");

    var c1 = new NodeContainer({ x: 300, y: 100 }, { w: 500, h: 500 });
    var c2 = new NodeContainer({ x: 20, y: 20 }, { w: 150, h: 150 });

    c1.addChildren(c2);
    c1.addChildren(node3);

    c2.addChildren(node1);
    c2.addChildren(node2);

    window.setTimeout(function () {
        diagram.addElement(node1);
        diagram.addElement(node2);
        diagram.addElement(node3);
    }, 1000);
    
    diagram.addElement(c1);
    diagram.addElement(c2);
    

</script>
